﻿#If WINDOWS_PHONE Then

Namespace Global.System.Text

    Public NotInheritable Class DBCSEncoding
        Inherits Encoding

        Private Shared _cache As Dictionary(Of String, Tuple(Of Char(), UInt16()))

        Private _dbcsToUnicode As Char() = Nothing
        Private _unicodeToDbcs As UInt16() = Nothing
        Private _webName As String = Nothing

        Private Const LEAD_BYTE_CHAR As Char = ChrW(65534)

        Public Overrides ReadOnly Property WebName() As String
            Get
                Return Me._webName
            End Get
        End Property

        Shared Sub New()
            If Not BitConverter.IsLittleEndian Then
                Throw New PlatformNotSupportedException("Not supported big endian platform.")
            End If
            DBCSEncoding._cache = New Dictionary(Of String, Tuple(Of Char(), UInt16()))
        End Sub

        Private Sub New()
            Me._dbcsToUnicode = Nothing
            Me._unicodeToDbcs = Nothing
            Me._webName = Nothing
        End Sub

        Public Overrides Function GetByteCount(ByVal chars As Char(), ByVal index As Integer, ByVal count As Integer) As Integer
            Dim byteCount As Integer = 0
            For i As Integer = 0 To count - 1
                Dim c As Char = chars(index)
                Dim u As UInt16 = Me._unicodeToDbcs(AscW(c))
                If u > &HFF Then
                    byteCount += 1
                End If
                index += 1
                byteCount += 1
            Next
            Return byteCount
        End Function

        Public Overrides Function GetBytes(ByVal chars As Char(), ByVal charIndex As Integer, ByVal charCount As Integer, ByVal bytes As Byte(), ByVal byteIndex As Integer) As Integer
            Dim byteCount As Integer = 0
            For i As Integer = 0 To charCount - 1
                Dim c As Char = chars(charIndex)
                Dim u As UInt16 = Me._unicodeToDbcs(AscW(c))
                If u = 0 AndAlso c <> ChrW(0) Then
                    bytes(byteIndex) = &H3F
                ElseIf u < &H100 Then
                    bytes(byteIndex) = CByte(u)
                Else
                    bytes(byteIndex) = CByte(((u >> 8) And &HFF))
                    byteIndex += 1
                    byteCount += 1
                    bytes(byteIndex) = CByte(u And &HFF)
                End If
                charIndex += 1
                byteIndex += 1
                byteCount += 1
            Next
            Return byteCount
        End Function

        Public Overrides Function GetCharCount(ByVal bytes As Byte(), ByVal index As Integer, ByVal count As Integer) As Integer
            Return Me.GetCharCount(bytes, index, count, Nothing)
        End Function

        Private Overloads Function GetCharCount(ByVal bytes As Byte(), ByVal index As Integer, ByVal count As Integer, ByVal decoder As DBCSDecoder) As Integer
            Dim charCount As Integer = 0
            Dim i As Integer
            For i = 0 To count - 1
                Dim u As UInt16 = 0
                If ((Not decoder Is Nothing) AndAlso (decoder.pendingByte <> 0)) Then
                    u = decoder.pendingByte
                    decoder.pendingByte = 0
                End If
                u = CUShort(((u << 8) Or bytes(index)))
                Dim c As Char = Me._dbcsToUnicode(u)
                If AscW(c) = &HFFFE Then
                    If (i < (count - 1)) Then
                        index += 1
                        i += 1
                    ElseIf (Not decoder Is Nothing) Then
                        decoder.pendingByte = bytes(index)
                        Return charCount
                    End If
                End If
                index += 1
                charCount += 1
            Next i
            Return charCount
        End Function

        Public Overrides Function GetChars(ByVal bytes As Byte(), ByVal byteIndex As Integer, ByVal byteCount As Integer, ByVal chars As Char(), ByVal charIndex As Integer) As Integer
            Return Me.GetChars(bytes, byteIndex, byteCount, chars, charIndex, Nothing)
        End Function

        Private Overloads Function GetChars(ByVal bytes As Byte(), ByVal byteIndex As Integer, ByVal byteCount As Integer, ByVal chars As Char(), ByVal charIndex As Integer, ByVal decoder As DBCSDecoder) As Integer
            Dim charCount As Integer = 0
            Dim i As Integer
            For i = 0 To byteCount - 1
                Dim u As UInt16 = 0
                If ((Not decoder Is Nothing) AndAlso (decoder.pendingByte <> 0)) Then
                    u = decoder.pendingByte
                    decoder.pendingByte = 0
                End If
                u = CUShort(((u << 8) Or bytes(byteIndex)))
                Dim c As Char = Me._dbcsToUnicode(u)
                If AscW(c) = &HFFFE Then
                    If i >= byteCount - 1 Then
                        If (Not decoder Is Nothing) Then
                            decoder.pendingByte = bytes(byteIndex)
                            Return charCount
                        End If
                        c = ChrW(0)
                    Else
                        byteIndex += 1
                        i += 1
                        u = CUShort(((u << 8) Or bytes(byteIndex)))
                        c = Me._dbcsToUnicode(u)
                    End If
                End If
                If ((c = ChrW(0)) AndAlso (u <> 0)) Then
                    chars(charIndex) = "?"c
                Else
                    chars(charIndex) = c
                End If
                byteIndex += 1
                charIndex += 1
                charCount += 1
            Next i
            Return charCount
        End Function

        Public Shared Function GetDBCSEncoding(ByVal name As String) As DBCSEncoding
            name = name.ToLower()
            Dim encoding As New DBCSEncoding() With
                {
                    ._webName = name
                }
            If DBCSEncoding._cache.ContainsKey(name) Then
                Dim tuple = DBCSEncoding._cache(name)
                encoding._dbcsToUnicode = tuple.Item1
                encoding._unicodeToDbcs = tuple.Item2
                Return encoding
            End If
            Dim dbcsToUnicode As Char() = New Char(&H10000 - 1) {}
            Dim unicodeToDbcs As UInt16() = New UInt16(&H10000 - 1) {}
            Using stream As Stream = GetType(DBCSEncoding).Assembly.GetManifestResourceStream(GetType(DBCSEncoding).Assembly.GetManifestResourceNames.Single(Function(s) s.EndsWith(name & ".bin")))
                Using reader As BinaryReader = New BinaryReader(stream)
                    For i As Integer = 0 To &HFFFF - 1
                        unicodeToDbcs(i) = reader.ReadUInt16()
                    Next
                    For i As Integer = 0 To &HFFFF - 1
                        Dim u = reader.ReadUInt16()
                        dbcsToUnicode(i) = ChrW(u)
                    Next
                End Using
            End Using
            DBCSEncoding._cache(name) = New Tuple(Of Char(), UInt16())(dbcsToUnicode, unicodeToDbcs)
            encoding._dbcsToUnicode = dbcsToUnicode
            encoding._unicodeToDbcs = unicodeToDbcs
            Return encoding
        End Function

        Public Overrides Function GetDecoder() As Decoder
            Return New DBCSDecoder(Me)
        End Function

        Public Overrides Function GetMaxByteCount(ByVal charCount As Integer) As Integer
            If (charCount < 0) Then
                Throw New ArgumentOutOfRangeException("charCount")
            End If
            Dim count As Long = (charCount + 1)
            count = (count * 2)
            If (count > &H7FFFFFFF) Then
                Throw New ArgumentOutOfRangeException("charCount")
            End If
            Return CInt(count)
        End Function

        Public Overrides Function GetMaxCharCount(ByVal byteCount As Integer) As Integer
            If (byteCount < 0) Then
                Throw New ArgumentOutOfRangeException("byteCount")
            End If
            Dim count As Long = (byteCount + 3)
            If (count > &H7FFFFFFF) Then
                Throw New ArgumentOutOfRangeException("byteCount")
            End If
            Return CInt(count)
        End Function

        Private NotInheritable Class DBCSDecoder
            Inherits Decoder

            Private _encoding As DBCSEncoding = Nothing
            Public pendingByte As Byte

            Public Sub New(ByVal encoding As DBCSEncoding)
                Me._encoding = encoding
            End Sub

            Public Overrides Function GetCharCount(ByVal bytes As Byte(), ByVal index As Integer, ByVal count As Integer) As Integer
                Return Me._encoding.GetCharCount(bytes, index, count, Me)
            End Function

            Public Overrides Function GetChars(ByVal bytes As Byte(), ByVal byteIndex As Integer, ByVal byteCount As Integer, ByVal chars As Char(), ByVal charIndex As Integer) As Integer
                Return Me._encoding.GetChars(bytes, byteIndex, byteCount, chars, charIndex, Me)
            End Function

        End Class

    End Class

End Namespace

#End If